package com.duojuhe.basic.login.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.support.spring.PropertyPreFilters;
import com.duojuhe.basic.captcha.service.KaptchaService;
import com.duojuhe.basic.login.dto.admin.AdminLoginReq;
import com.duojuhe.basic.login.dto.admin.AdminLoginRes;
import com.duojuhe.basic.login.service.LoginService;
import com.duojuhe.cache.LoginUserTokenCache;
import com.duojuhe.common.bean.SystemMenuInfoVo;
import com.duojuhe.common.bean.UserTokenInfoVo;
import com.duojuhe.common.config.DuoJuHeConfig;
import com.duojuhe.common.constant.SystemConstants;
import com.duojuhe.common.enums.IdResourceEnum;
import com.duojuhe.common.enums.SystemEnum;
import com.duojuhe.common.enums.log.LogEnum;
import com.duojuhe.common.enums.user.UserEnum;
import com.duojuhe.common.exception.base.DuoJuHeException;
import com.duojuhe.common.result.ErrorCodes;
import com.duojuhe.common.result.ServiceResult;
import com.duojuhe.common.utils.encryption.md5.MD5Util;
import com.duojuhe.common.utils.idgenerator.UUIDUtils;
import com.duojuhe.common.utils.iputil.IPUtils;
import com.duojuhe.common.utils.page.PageHelperUtil;
import com.duojuhe.common.utils.thread.ThreadUtils;
import com.duojuhe.common.utils.token.TokenUtils;
import com.duojuhe.coremodule.BaseService;
import com.duojuhe.coremodule.system.entity.SystemLog;
import com.duojuhe.coremodule.system.entity.SystemTenant;
import com.duojuhe.coremodule.system.entity.SystemUser;
import com.duojuhe.coremodule.system.mapper.SystemLogMapper;
import com.duojuhe.coremodule.system.mapper.SystemMenuMapper;
import com.duojuhe.coremodule.system.mapper.SystemTenantMapper;
import com.duojuhe.coremodule.system.mapper.SystemUserMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.List;
import java.util.Objects;

@Slf4j
@Service
public class LoginServiceImpl extends BaseService implements LoginService {
    @Resource
    private KaptchaService kaptchaService;
    @Resource
    private DuoJuHeConfig duoJuHeConfig;
    @Resource
    private LoginUserTokenCache loginUserTokenCache;
    @Resource
    private SystemUserMapper systemUserMapper;
    @Resource
    private SystemMenuMapper systemMenuMapper;
    @Resource
    private SystemLogMapper systemLogMapper;
    @Resource
    private SystemTenantMapper systemTenantMapper;

    /**
     * admin管理用户登录
     *
     * @param req
     * @return
     */
    @Override
    public ServiceResult adminLogin(AdminLoginReq req) {
        String loginName = req.getLoginName();
        try {
            //校验图形验证码
            kaptchaService.verifyCaptchaCode(req.getImageId(), req.getImageCode());
            SystemUser systemUser = systemUserMapper.querySystemUserByLoginName(loginName);
            if (systemUser == null) {
                return ServiceResult.fail(ErrorCodes.LOGIN_NAME_OR_PASSWORD_ERROR);
            }
            if (!SystemEnum.STATUS.NORMAL.getKey().equals(systemUser.getStatusCode())) {
                return ServiceResult.fail(ErrorCodes.USER_FORBID_USE);
            }
            if (!MD5Util.getMD532(req.getPassword()).equals(systemUser.getPassword())) {
                return ServiceResult.fail(ErrorCodes.LOGIN_NAME_OR_PASSWORD_ERROR);
            }
            //检查用户所在租户是否存在
            SystemTenant systemTenant = systemTenantMapper.selectByPrimaryKey(systemUser.getTenantId());
            if (systemTenant == null) {
                return ServiceResult.fail(ErrorCodes.TENANT_FORBID_USE);
            }
            if (!SystemEnum.STATUS.NORMAL.getKey().equals(systemTenant.getStatusCode())) {
                return ServiceResult.fail(ErrorCodes.TENANT_FORBID_USE);
            }
            HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
            //获取ip当前客户
            String loginIp = IPUtils.getClientIPByIpHeader(request, duoJuHeConfig.getGetAdminIpHeader());
            //当前时间
            Date nowDate = new Date();
            //token 过期时间 默认30分钟有效
            int tokenExpiresIn = duoJuHeConfig.getTokenExpiresIn();
            // 当验证都通过后，把用户信息放在缓存里
            UserTokenInfoVo userTokenInfoVo = new UserTokenInfoVo();
            userTokenInfoVo.setUserId(systemUser.getUserId());
            userTokenInfoVo.setLoginName(systemUser.getLoginName());
            userTokenInfoVo.setCreateTime(systemUser.getCreateTime());
            userTokenInfoVo.setUserNumber(systemUser.getUserNumber());
            userTokenInfoVo.setTenantId(systemUser.getTenantId());
            userTokenInfoVo.setTenantNumber(systemTenant.getTenantNumber());
            userTokenInfoVo.setTenantName(systemTenant.getTenantName());
            userTokenInfoVo.setTenantAbbreviation(systemTenant.getTenantAbbreviation());
            userTokenInfoVo.setSm4Key(systemTenant.getSm4Key());
            userTokenInfoVo.setRealName(systemUser.getRealName());
            userTokenInfoVo.setRoleId(systemUser.getRoleId());
            userTokenInfoVo.setPostId(systemUser.getPostId());
            userTokenInfoVo.setCreateDeptId(systemUser.getCreateDeptId());
            userTokenInfoVo.setMenuInfoVOList(queryAdminUserMenuVOList(systemUser));
            userTokenInfoVo.setUserTypeCode(systemUser.getUserTypeCode());
            userTokenInfoVo.setUserModuleCode(systemUser.getModuleCode());
            userTokenInfoVo.setUserTypeName(UserEnum.USER_TYPE.getValueByKey(systemUser.getUserTypeCode()));
            userTokenInfoVo.setLoginIp(loginIp);
            userTokenInfoVo.setIpRegion(IPUtils.getIpInfo(loginIp));
            userTokenInfoVo.setLoginTime(nowDate);
            userTokenInfoVo.setKickOut(false);
            userTokenInfoVo.setSignKey(UUIDUtils.getUUID32());
            userTokenInfoVo.setUserLoginSource(SystemEnum.LOGIN_RESOURCE.SYSTEM_ADMIN.getKey());
            userTokenInfoVo.setUserIdSource(IdResourceEnum.IdResource.system_user.getKey());
            userTokenInfoVo.setToken(userTokenInfoVo.getUserLoginSource() + TokenUtils.createHeaderToken());
            loginUserTokenCache.putUserTokenInfoVoToCache(userTokenInfoVo.getToken(), userTokenInfoVo, tokenExpiresIn);
            ServiceResult serviceResult = ServiceResult.ok(new AdminLoginRes(userTokenInfoVo));
            //保存日志
            String methodName = "com.duojuhe.common.login.controller.LoginController.adminLogin()";
            saveOperationLog(userTokenInfoVo, methodName, req, serviceResult, request);
            return serviceResult;
        } catch (Exception e) {
            if (e instanceof DuoJuHeException) {
                log.warn("后台用户登录出现异常：loginName={},Exception={}", loginName, e.getMessage());
                return ServiceResult.fail(e.getMessage());
            } else {
                log.error("后台用户登录出现异常：loginName={},Exception={}", loginName, e);
                return ServiceResult.fail("登录出现异常!");
            }
        }
    }

    /**
     * 获取用户拥有的权限菜单
     */
    private List<SystemMenuInfoVo> queryAdminUserMenuVOList(SystemUser systemUser) {
        //用户类型
        String userTypeCode = systemUser.getUserTypeCode();
        //单独排序
        PageHelperUtil.defaultOrderBy("sort desc,createTime desc,menuId desc");
        if (UserEnum.USER_TYPE.SUPER_ADMIN.getKey().equals(userTypeCode)) {
            //超管用户
            return systemMenuMapper.querySystemMenuInfoVoListByRoleId("");
        } else if (UserEnum.USER_TYPE.TENANT_ADMIN.getKey().equals(userTypeCode)) {
            //租户管理人员
            return systemMenuMapper.querySystemTenantAllSystemMenuInfoVoListByTenantId(systemUser.getTenantId());
        } else {
            String roleId = StringUtils.isBlank(systemUser.getRoleId()) ? SystemConstants.UNKNOWN_ID : systemUser.getRoleId();
            return systemMenuMapper.querySystemMenuInfoVoListByRoleId(roleId);
        }
    }


    /**
     * 保存日志
     */
    private void saveOperationLog(UserTokenInfoVo userTokenInfoVo, String methodName, Object requestParameter, ServiceResult returnResult, HttpServletRequest request) {
        //返回结果
        PropertyPreFilters.MySimplePropertyPreFilter filter = new PropertyPreFilters().addFilter().addExcludes(SystemConstants.EXCLUDE_PROPERTIES);
        //当前登录ip
        String loginIp = userTokenInfoVo.getLoginIp();
        //日志对象
        SystemLog systemLog = new SystemLog();
        //主键
        systemLog.setLogId(UUIDUtils.getUUID32());
        //创建人
        systemLog.setCreateUserId(userTokenInfoVo.getUserId());
        //创建部门id
        systemLog.setCreateDeptId(userTokenInfoVo.getCreateDeptId());
        //租户id
        systemLog.setTenantId(userTokenInfoVo.getTenantId());
        //操作人登录名
        systemLog.setLoginName(userTokenInfoVo.getLoginName());
        //操作人真实姓名
        systemLog.setCreateUserName(userTokenInfoVo.getRealName());
        //创建时间
        systemLog.setCreateTime(new Date());
        //请求方法名
        systemLog.setMethodName(methodName);
        // 设置请求方式
        systemLog.setRequestMethod(request.getMethod());
        //请求模块名称
        systemLog.setModuleName(LogEnum.ModuleName.USER_LOGIN.getValue());
        //操作类型
        systemLog.setOperationTypeCode(LogEnum.OperationType.LOGIN.getKey());
        //备注
        systemLog.setRemark(LogEnum.OperationType.getValueByKey(LogEnum.OperationType.LOGIN.getKey()));
        //请求url
        systemLog.setRequestUrl(request.getRequestURI());
        // 请求ip
        systemLog.setOperationIp(loginIp);
        //操作ip的区域
        systemLog.setIpRegion(userTokenInfoVo.getIpRegion());
        //请求参数
        systemLog.setRequestParameter(JSONObject.toJSONString(requestParameter, filter));
        //返回结果
        systemLog.setReturnResult(JSONObject.toJSONString(returnResult, filter));
        //操作状态
        systemLog.setOperationStatusCode(SystemEnum.OPERATION_STATUS.SUCCESS.getKey());
        // 添加到队列中
        log.info("【用户登录操作日志】{}", systemLog.toString());
        // 异步添加到数据库
        ThreadUtils.execute(() -> systemLogMapper.insertSelective(systemLog));
    }
}
